const EventEmitter = require("events");
const Koa = require("koa");
const KSR = require("koa-static-resolver");
const http = require("http");
const socketIO = require("socket.io");
const fs = require("fs");

const Util = require("../util/util")
const openBrowser = require("../util/openBrowser");
const cache = require("../util/cache").init();
const readdir = require("../util/readdir");
const Course = require("./start-course");

class GUI extends EventEmitter {
    constructor(port) {
        super();
        this.port = port;
        this.startServer();
    }

    startServer() {
        const config = cache.config || {};
        const host = config.host;
        const root = cache.root;
        const port = this.port;

        //用koa启动一个服务器，然后设置静态路径为ui，等koa启动之后，打开浏览器
        const app = new Koa();

        app.use(KSR({
            dirs: [
                __dirname + "/ui/",
                `${root}`

            ],
            maxAge: 10,
            gzip: true,
            cache: {}
        }));

        const server = http.createServer(app.callback());
        this.listenSocketIO(server);



        server.listen(port, () => {

            // Open browser
            const url = `http://${host}:${port}`
            if (config.headless) {
                console.log(port)
            } else {
                openBrowser(url);
            }

        });
    }

    listenSocketIO(server) {
        const io = socketIO(server);
        io.on("connection", (socket) => {

            socket.on("data", (data) => {

                this.socketDataHandler(data);
            });

            socket.on("disconnect", function() {
                console.log(new Date().toLocaleString() + " a GUI user disconnected");
            });

        });

        this.sockets = io.sockets;
    }

    socketDataHandler(data) {
        if (!data) {
            return;
        }

        var handlers = {
            //only for text message
            message: this.messageHandler,
            //only for bd cmd
            cmd: this.cmdHandler,
            //for data function 
            data: this.dataHandler
        };

        var handler = handlers[data.type];
        if (handler) {
            handler.call(this, data.data);
        }

    }

    messageHandler(message) {
        //this.sendMessage(message);
    }

    sendMessage(data) {
        this.sockets.emit("message", data)
    }

    cmdHandler(cmd) {
        if (!cmd) {
            return;
        }
        if (this.course) {
            //this.course.close();
            this.course = null;
        }
        this.course = new Course(this, cmd);
    }

    dataHandler(data) {
        if (!data || !data.name) {
            console.log("invalid data arguments");
            return;
        }
        var handler = this[data.name];
        if (typeof(handler) === "function") {
            console.log("data handler: " + data.name);
            handler.call(this, data.data);
        } else {
            var msg = "Not found handler: " + data.name;
            console.log(msg);
            this.sendMessage(msg, "#ff0000");
        }
    }

    getReadme() {}
    getTestSpecs() {}
    getTabs() {}

    async getProjectInfo() {
        var pc = cache.config;

        var repository = "";
        if (pc.repository && pc.repository.url) {
            repository = pc.repository.url;
        }


        var info = {
            project: Util.formatPath(Util.root),
            repository: repository,
            name: "updateProjectInfo",
            version: pc.version,
            list: this.getComponentList()
        };

        //Util.initGitHooks();

        //console.log(JSON.stringify(info, null, 2));
        this.sockets.emit("data", {
            data: info,
            name: "updateProjectInfo",
            type: "data",

        });

    }

    getComponentList() {
        var componentsPath = cache.projectRoot + "/components/";
        var folderNames = fs.readdirSync(componentsPath);
        const list = [];
        folderNames.forEach(function(folderName) {
            var states = fs.statSync(componentsPath + folderName);
            //console.log(states.isDirectory());
            if (states.isDirectory()) {
                var packagePath = componentsPath + folderName + "/package.json";
                if (fs.existsSync(packagePath)) {
                    list.push(folderName);
                }
            }
        });

        return list;

    }


    async getBrowseInfo(browsePath) {
        browsePath = path.resolve(browsePath || cache.projectRoot);
        if (!fs.existsSync) {
            browsePath = cache.projectRoot;
        }

        var data = await this.generateBrowseInfo(browsePath);

        console.log("updateBrowseInfo", data);

        this.sendData({
            name: "updateBrowseInfo",
            data: data
        });

    }

    async generateBrowseInfo(browsePath) {

        var dirs = [];
        var files = [];
        var list = await readdir(browsePath);
        for (let subName of list) {
            var subPath = path.resolve(browsePath, subName);
            var stats = await this.stat(subPath);
            if (!stats) {
                continue;
            }
            if (stats.isFile()) {
                await this.generateBrowseFileItem(subName, stats, files);
                continue;
            }
            if (stats.isDirectory()) {
                await this.generateBrowseDirItem(subName, stats, dirs, browsePath);
            }
        }

        var rows = [{
            name: "../",
            icon: "up",
            unsorted: true,
            unsortedTop: true
        }];

        rows = rows.concat(dirs);
        rows = rows.concat(files);

        var gridData = {
            option: {
                sortField: "name",
                frozenColumn: 1
            },
            columns: [{
                id: "icon",
                name: "",
                dataType: "icon",
                resizable: false,
                width: 35
            }, {
                id: "name",
                name: "Name",
                width: 200,
                dataType: "string",
                formatter: "string"
            }, {
                id: "mtime",
                name: "Date modified",
                dataType: "date",
                width: 150
            }, {
                id: "size",
                name: "Size"
            }],
            rows: rows
        };

        var data = {
            browsePath: browsePath,
            gridData: gridData,
            packageJson: files.length
        };

        return data;
    }


    async generateBrowseFileItem(subName, stats, files) {
        if (subName !== "package.json") {
            return;
        }
        files.push({
            icon: "json",
            name: subName,
            unsorted: true,
            size: Util.BF(stats.size),
            mtime: stats.mtime.toLocaleString()
        });
    }

    async generateBrowseDirItem(subName, stats, dirs, browsePath) {
        if (subName.indexOf(".") === 0) {
            return;
        }
        var hasProject = await this.checkBrowseProject(browsePath + "/" + subName);
        if (!hasProject) {
            return;
        }
        dirs.push({
            icon: "dir",
            name: subName,
            mtime: stats.mtime.toLocaleString()
        });
    }

    async checkBrowseProject(folderPath) {

        var isProject = (subName, stats) => {
            if (stats.isFile() && subName === "package.json") {
                return true;
            }
            if (stats.isDirectory() && subName.indexOf(".") !== 0) {
                return true;
            }
            return false;
        };

        var list = await readdir(folderPath);
        for (let subName of list) {
            var subPath = path.resolve(folderPath, subName);
            var stats = await this.stat(subPath);
            if (!stats) {
                continue;
            }
            if (isProject(subName, stats)) {
                return true;
            }
        }
        return false;
    }

    async stat(p) {
        return new Promise((resolve) => {
            fs.stat(p, (err, stats) => {
                if (err) {
                    //console.log("ERROR: fs.stat: " + p);
                    resolve(null);
                    return;
                }
                resolve(stats);
            });
        });
    }
};

module.exports = GUI;